package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SearchService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    @Override
    public Map search(Map<String, String> searchMap) {
        HashMap<String, Object> resultMap = new HashMap<>();
        if (searchMap != null) {
            //构建查询条件封装对象
            NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

            //需求1:根据关键字进行查询
            if (StringUtils.isNotEmpty(searchMap.get("keywords"))){
                //对条件进行分词查询
                boolQueryBuilder.must(QueryBuilders.matchQuery("name",searchMap.get("keywords")).operator(Operator.AND));
            }
            //需求2:按照品牌过滤查询
            if (StringUtils.isNotEmpty(searchMap.get("brand"))){
                //不进行分词查询
                boolQueryBuilder.filter(QueryBuilders.termQuery("brandName",searchMap.get("brand")));
            }

            //需求3:品牌(分组)聚合查询  skuBrand为自定义名称,用于将来获取数据   brandName为分组的字段
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrand").field("brandName"));
            //需求4:按照规格进行过滤查询
            for (String key : searchMap.keySet()) {
                //判断是否以spec_开头
                if (key.startsWith("spec_")){
                    //对规格信息进行处理
                    String value = searchMap.get(key).replace("%2B", "+");
                    //进行过滤查询
                    boolQueryBuilder.filter(QueryBuilders.termQuery("specMap."+key.substring(5)+".keyword",value));
                }
            }
            //需求5:规格(分组)聚合查询
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword"));
            //需求6:按照价格进行区间查询
            if (StringUtils.isNotEmpty(searchMap.get("price"))){
                //截取条件
                String[] prices = searchMap.get("price").split("-");
                //判断截取结果是否有两个
                if (prices.length==2){
                    //如果是设置价格上限
                    boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(prices[1]));
                }
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(prices[0]));
            }
            //需求7:分页
            String pageNum = searchMap.get("pageNum");//当前页
            String pageSize = searchMap.get("pageSize");//没页条数
            if (StringUtils.isEmpty(pageNum)){
                pageNum = "1";//如果pageNum没有默认为1
            }
            if (StringUtils.isEmpty(pageSize)){
                pageSize = "20";//如果pageSize没有默认为20
            }
            //进行分页查询
            nativeSearchQueryBuilder.withPageable(PageRequest.of(Integer.parseInt(pageNum)-1,Integer.parseInt(pageSize)));
            //需求8:排序
            if (StringUtils.isNotEmpty(searchMap.get("sortFiled"))&&StringUtils.isNotEmpty(searchMap.get("sortRule"))){
                if ("ASC".equals(searchMap.get("sortRule"))){
                    //升序
                    nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(searchMap.get("sortFiled")).order(SortOrder.ASC));
                }else {
                    //降序
                    nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(searchMap.get("sortFiled")).order(SortOrder.DESC));
                }
            }
            //需求9:高亮查询
            HighlightBuilder.Field field = new HighlightBuilder
                    //设置三要素
                    .Field("name")
                    .preTags("<span style='color:red'>")
                    .postTags("</span>");
            nativeSearchQueryBuilder.withHighlightFields(field);
            nativeSearchQueryBuilder.withQuery(boolQueryBuilder);

        /**
         * 第一个参数: 条件构建对象
         * 第二个参数: 查询操作实体类
         * 第三个参数: 查询结果操作对象
         */
         AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
            @Override
             public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                ArrayList<T> list = new ArrayList<>();
                SearchHits hits = searchResponse.getHits();
                for (SearchHit hit : hits) {
                    //获取json格式的数据
                    String sourceAsString = hit.getSourceAsString();
                    //转化为java格式
                    SkuInfo skuInfo = JSON.parseObject(sourceAsString, SkuInfo.class);
                    //将name替换为高亮name
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                    if (null != highlightFields && highlightFields.size() > 0) {
                        Text[] highlightName = highlightFields.get("name").getFragments();
                        //替换
                        skuInfo.setName(highlightName[0].toString());
                    }
                    //添加到list集合
                    list.add((T) skuInfo);
                }
                //                             结果集合 分页对象    总记录条数
                return new AggregatedPageImpl<T>(list,pageable,hits.getTotalHits(),searchResponse.getAggregations());
                }
            });
            //总记录数
            resultMap.put("total",aggregatedPage.getTotalElements());
            //总页数
            resultMap.put("totalPages",aggregatedPage.getTotalPages());
            //结果集
            resultMap.put("row",aggregatedPage.getContent());
            //获取聚合结果
            //第一种
            /*StringTerms skuBrand = (StringTerms) aggregatedPage.getAggregation("skuBrand");
            List<String> brandList =skuBrand.getBuckets().stream().map(bucket ->
                    bucket.getKeyAsString()).collect(Collectors.toList());*/

            // 获取品牌聚合结果
            Terms skuBrand = (Terms) aggregatedPage.getAggregations().asMap().get("skuBrand");
            List<? extends Terms.Bucket> buckets = skuBrand.getBuckets();
            //创建list集合
            List brandList = new ArrayList<>();
            for (Terms.Bucket bucket : buckets) {
                Object key = bucket.getKey();
                //拿到每个品牌信息,存入集合
                brandList.add(key);
            }
            //获取规格聚合结果
            StringTerms skuSpec = (StringTerms) aggregatedPage.getAggregation("skuSpec");
            List<String> specList = skuSpec.getBuckets().stream().map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
            //创建map集合,用于封装聚合结果
            HashMap<String, Set<String>> specMaps = new HashMap<>();
            //遍历list规格结果集
            for (String spec : specList) {
                //将结果转为map集合
                Map<String,String> specMap = JSON.parseObject(spec, Map.class);
                Set<Map.Entry<String, String>> entrySet = specMap.entrySet();
                //遍历得到每条map数据
                for (Map.Entry<String, String> stringEntry : entrySet) {
                    String key = stringEntry.getKey();//得到key
                    String value = stringEntry.getValue();//得到value
                    //根据当key得到specMaps中的值
                    Set<String> specValue = specMaps.get(key);
                    //判断值是否为空
                    if (specValue == null){
                        //如果为空则将当前specMap中的value添加到set集合中
                        specValue = new HashSet<>();
                    }
                    specValue.add(value);
                    //添加到specMaps
                    specMaps.put(key,specValue);
                }
            }

            //品牌聚合结果
            resultMap.put("specMaps",specMaps);
            //规格聚合结果
            resultMap.put("specList",specList);
            //当前页
            resultMap.put("pageNum",pageNum);
            return resultMap;
        }
        return null;
    }
}
